package com.yys.seckill.service;

import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import com.yys.seckill.dao.SeckillDao;
import com.yys.seckill.dao.SuccessKilledDao;
import com.yys.seckill.dto.Exposer;
import com.yys.seckill.dto.SeckillExecution;
import com.yys.seckill.entity.Seckill;
import com.yys.seckill.entity.SuccessKilled;
import com.yys.seckill.enums.SeckillStateEnum;
import com.yys.seckill.exception.RepeatKillException;
import com.yys.seckill.exception.SeckillCloseException;
import com.yys.seckill.exception.SeckillException;

@Service
public class SeckillServiceImpl implements SeckillService {
	
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private SeckillDao seckillDao;
	
	@Autowired
	private SuccessKilledDao successKilledDao;
	
	@Override
	public List<Seckill> getSeckillList() {
		return seckillDao.queryAll(0, 10);
	}

	@Override
	public Seckill getSeckill(long seckillId) {
		return seckillDao.queryByPrimaryKey(seckillId);
	}

	@Override
	public Exposer exportSeckillUrl(long seckillId) {
		Seckill seckill = seckillDao.queryByPrimaryKey(seckillId);
		if (seckill == null)
			return new Exposer(false, seckillId);
		Date startTime = seckill.getStartTime();
		Date endTime = seckill.getEndTime();
		Date now = new Date();
		if (now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime())
			return new Exposer(false, seckillId, startTime.getTime(), endTime.getTime(), now.getTime());
		String md5 = getMd5(seckillId);
		return new Exposer(true, md5, seckillId);
	}

	@Override
	@Transactional
	/**
	 * 使用注解控制事务方法的有点：
	 * 1.开发团队达成一致约定，明确标注事务方法的编程风格
	 * 2.保证事务方法的执行时间尽可能短，不要穿插其他网络操作RPC／HTTP请求或者将其剥离到事务方法外部
	 * 3.不是所有方法都需要事务，如果只有一条修改操作，或只读操作，则不需要事务
	 */
	public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
			throws SeckillException, RepeatKillException, SeckillCloseException {
		// 校验md5值
		if (md5 == null || !md5.equals(getMd5(seckillId))) {
			throw new SeckillException("data is rewrited");
		}

		try {
			// 执行秒杀
			// 减库存 ＋ 记录秒杀明细
			Date now = new Date();
			int updateCount = seckillDao.reduceNumber(seckillId, now);
			if (updateCount <= 0) {
				// 减库存失败，即秒杀结束
				throw new SeckillCloseException("seckill is closed");
			} else {
				// 减库存成功，记录秒杀明细
				int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
				if (insertCount <= 0) {
					// 插入购买明细失败，即重复秒杀
					throw new RepeatKillException("repeat seckill");
				} else {
					// 秒杀成功
					SuccessKilled successKilled = successKilledDao.queryByPrimaryKeyWithSeckill(seckillId, userPhone);
					return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
				}
			}
		} catch (SeckillCloseException e1) {
			throw e1;
		} catch (RepeatKillException e2) {
			throw e2;
		} catch (Exception e) {
			// 将编译期异常转化成运行期异常
			logger.error(e.getMessage(), e);
			throw new SeckillException("system inner error:" + e.getMessage());
		}
	}
	
	private String slat = "iioaARE?a~`9&#2.]|=!2*AEo(:./adi*&%*|:;.";

	private String getMd5(long seckillId){
		String base = seckillId + "/" + slat;
		String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
		return md5;
	}
	
}
